mod ffmpeg;
use core_api::db;
use ffmpeg::FfmpegController;

use std::{env, path::Path, sync::Mutex};

use lazy_static::lazy_static;

pub fn get_args() -> Vec<String> {
    // 获取命令行参数（包括程序名）
    let args: Vec<String> = env::args().collect();
    log::info!("ff7scre args: {:?}", &args);
    args
}

pub fn run_sync(args: Vec<String>) -> i32 {
    let mut fc = FfmpegController::new();
    let ret = fc.run_sync(args);
    //println!("=== Sync Logs ===");
    for line in fc.get_logs() {
        println!("{}", line);
    }
    ret
}

pub fn run_async(args: Vec<String>) -> i32 {
    let mut fc = FfmpegController::new();
    let log_rx = fc.run_async(args);
    let log_thread = std::thread::spawn(move || {
        for line in log_rx {
            println!("[Async Log] {}", line);
        }
        println!("Log thread exiting");
    });
    std::thread::sleep(std::time::Duration::from_secs(101));
    // 等待ffmpeg 退出
    let ret = fc.wait_stop();
    // 等待日志线程退出
    let _ = log_thread.join();
    println!("=== Async FFmpeg Stopped ===");
    ret
}

// === 示例代码 ===

pub fn sync_example() {
    let args = vec!["ffmpeg".to_string(), "-version".to_string()];
    run_sync(args);
}

pub fn async_example() {
    // libavdevice\gdigrab.c#gdigrab_read_packet
    // libavdevice\dshow.c # dshow_read_packet
    let args = vec![
        "ffmpeg".to_string(),
        "-y".to_string(),
        "-probesize".to_string(),
        "10M".to_string(),
        // 桌面输入
        "-f".to_string(),
        "gdigrab".to_string(),
        "-r".to_string(),
        "10".to_string(),
        "-t".to_string(),
        "28800".to_string(),
        "-i".to_string(),
        "desktop".to_string(),
        "-f".to_string(),
        "dshow".to_string(),
        "-i".to_string(),
        "video=\"FULL HD 1080P Webcam\"".to_string(), // 注意这里不加引号
        //"-f".to_string(), //这里和gdigrab 录屏时是一致的在ctr+alt+delete 会退出 gdigrab.c 需要重新打开 BitBlt
        //"dshow".to_string(),
        //"-i".to_string(),
        //"audio=\"麦克风 (Realtek USB2.0 MIC)\"".to_string(),
        "-filter_complex".to_string(),
        "\"[0]scale=1024:-1[desktop];[1]scale=160:120[cam];[desktop][cam]overlay=10:10\""
            .to_string(),
        // 编码与输出设置
        "-b:v".to_string(),
        "550k".to_string(),
        "-c:v".to_string(),
        "libx264".to_string(),
        "d:/output1.mkv".to_string(),
    ];

    let _args = vec![
    "ffmpeg".to_string(),
    "-hide_banner".to_string(),
    "-y".to_string(),
    "-f".to_string(),
    "gdigrab".to_string(),
    "-i".to_string(),
    "desktop".to_string(),
    "-r".to_string(),
    "10".to_string(),
    "-t".to_string(),
    "28800".to_string(),
    "-b:v".to_string(),
    "550k".to_string(),
    "-vf".to_string(),
    concat!(
        "\"scale=1024:-1,",
        "drawtext=fontfile=msyh.ttf:text='薛1 %{localtime}':fontsize=12:fontcolor=red:x=1:y=1,",
        "drawtext=fontfile=msyh.ttf:text='%{localtime}':x=mod(100*t\\,w):y=abs(sin(t))*h*0.9:box=0:fontcolor=gray@0.4\""
    ).to_string(),
    "test4.mkv".to_string(),
];

    run_async(args);
}

// 全局消息管理器
lazy_static! {
    static ref GMM: Mutex<Option<db::MessageManager>> = Mutex::new(None);
}
/// 发送消息
fn send_msg(message: db::Message) -> Result<(), String> {
    GMM.lock().unwrap().as_ref().unwrap().send_msg(message)
}

fn insert_device(key: &str, content: &str) -> Result<(), String> {
    GMM.lock()
        .unwrap()
        .as_ref()
        .unwrap()
        .insert_device(key, content)
}

fn update_device(id: u32) -> Result<(), String> {
    GMM.lock().unwrap().as_ref().unwrap().update_device(id)
}

fn delete_device() -> Result<(), String> {
    GMM.lock().unwrap().as_ref().unwrap().delete_device()
}

fn get_audio_video() -> (Option<String>, Option<String>) {
    GMM.lock().unwrap().as_ref().unwrap().get_audio_video()
}

///初始化 全局消息管理器 GMM
pub fn init_gmm() {
    let mut mm = db::MessageManager::new();
    mm.init(false);
    *GMM.lock().unwrap() = Some(mm);
}

/// 列出设备
pub fn list_devices() {
    // 删除配置
    let _ = delete_device();
    //let mut camara: Option<String> = None;
    //let mut mic: Option<String> = None;

    // 探测 摄像头和 麦克风
    let args = vec![
        "ffmpeg".to_string(),
        "-list_devices".to_string(),
        "true".to_string(),
        "-f".to_string(),
        "dshow".to_string(),
        "-i".to_string(),
        "dummy".to_string(),
    ];
    let mut fc = FfmpegController::new();
    fc.run_sync(args);
    let logs = fc.get_logs();
    //println!("=== Sync Logs ==={:?}",&logs);
    drop(fc);

    //const START_STR: &str = r#"] ""#; //ffmpeg7.0 中间有1个空格
    const START_STR: &str = r#"]  ""#; //ffmpeg4.4 中间有2个空格
    const END_STR: &str = r#"""#;
    //ffmpeg4.4 返回结果为1
    // [dshow @ 000001f3b006f9c0] "Integrated Camera" (video)
    // [dshow @ 000001f3b006f9c0] "外部麦克风 (Realtek(R) Audio)" (audio)
    let device_list = logs
        .iter()
        //.inspect(|line| println!("==>{}", line)) //输出调试
        .filter(|line| {
            let ok = line.starts_with("[dshow")
                && ((line.ends_with("(video)") || line.ends_with("(audio)")) // ffmpeg7.0
                            || (line.to_lowercase().contains("cam") || line.contains("麦克"))); // ffmpeg4.4

            ok
        })
        .map(|line| {
            if let Some(start) = line.find(START_STR) {
                return Some(&line[start + START_STR.len()..]);
            }
            None
        })
        .filter(|line| line.is_some())
        .map(|line| line.unwrap())
        //.inspect(|line| println!("==>{}", line)) //输出调试
        .collect::<Vec<&str>>();
    // 输出
    //Integrated Camera" (video)
    //外部麦克风 (Realtek(R) Audio)" (audio)
    //麦克风阵列 (Realtek(R) Audio)" (audio)

    for (index, device) in device_list.iter().enumerate() {
        log::info!(
            "device: {},AUDIO:{},VIDEO:{}",
            device,
            device.contains("麦克"),
            device.to_lowercase().contains("cam")
        );

        let key = if device.ends_with("(audio)") || device.contains("麦克") {
            format!("AUDIO_{}", index)
        } else if (device.ends_with("(video)") || device.to_lowercase().contains("cam"))
            && !device.contains("screen")
        {
            format!("VIDEO_{}", index)
        } else {
            continue; // 跳过不需要的设备
        };

        let end: usize = device.find(END_STR).unwrap();
        let device_name = &device[..end];

        //保存到数据库
        let r = insert_device(key.as_str(), device_name);
        log::info!("Insert device: {}, result: {:?}", key, r);
    }
}

pub fn test_audio(id: u32, device_name: &str) -> i32 {
    let audio_args = vec![
        "ffmpeg".to_string(),
        "-y".to_string(),
        "-loglevel".to_string(),
        "quiet".to_string(),
        "-f".to_string(),
        "dshow".to_string(),
        "-i".to_string(),
        format!("audio=\"{}\"", device_name),
        // r#"audio=外部麦克风 (Realtek(R) Audio)"#, //这里不用加双引号
        "-aframes".to_string(),
        "1".to_string(),
        "logs/testmic.aac".to_string(),
    ];

    let r = run_sync(audio_args);

    log::info!("AUDIO:[{}], result: {}", device_name, r);
    if r == 0 {
        let _ = update_device(id);
    }
    r
}

pub fn test_video(id: u32, device_name: &str) -> i32 {
    let video_args = vec![
        "ffmpeg".to_string(),
        "-y".to_string(),
        "-loglevel".to_string(),
        "quiet".to_string(),
        "-f".to_string(),
        "dshow".to_string(),
        "-i".to_string(),
        format!("video=\"{}\"", device_name),
        "-vframes".to_string(),
        "1".to_string(),
        "logs/testcamara.png".to_string(),
    ];

    let r = run_sync(video_args);

    log::info!("VIDEO:[{}], result: {}", device_name, r);
    if r == 0 {
        let _ = update_device(id);
    }
    r
}

pub fn start_recording(
    output_file: &Path,
    watermark_text: String,
    force_open_camera: bool,
) -> Result<u32, String> {
    let pid = std::process::id();

    let outfile = output_file.to_str().unwrap();

    let is_open_camera;
    let audio_video = get_audio_video();
    let args = match audio_video {
        // 有摄像头和麦克风
        (Some(audio), Some(video)) => {
            is_open_camera = true;
            // ffmpeg -y -probesize 10M -f gdigrab -r 10 -t 28800 -i desktop -f dshow -i video="RGB Camera" -f dshow -i audio="外部麦克风 (Realtek(R) Audio)" -filter_complex "[0]scale=1024:-1[desktop]; [1]scale=160:120[cam]; [desktop][cam]overlay=5:5,drawtext=text='%{localtime}':x=5:y=5:fontsize=12:fontcolor=red,drawtext=text='%{localtime}':x=mod(100*t\,w):y=abs(sin(t))*h*0.9:box=0:fontcolor=gray@0.4[video]; [2]anull[audio]"  -map "[video]" -map "[audio]" -b:v 550k -c:v libx264 -c:a aac all.mp4
            vec![
                "ffmpeg".to_string(),
                "-hide_banner".to_string(),
                "-y".to_string(),
                "-probesize".to_string(),
                "10M".to_string(),
                "-f".to_string(),
                "gdigrab".to_string(),
                "-r".to_string(),
                "10".to_string(),
                "-t".to_string(),
                "28800".to_string(),
                "-i".to_string(),
                "desktop".to_string(),
                "-f".to_string(),
                "dshow".to_string(),
                "-i".to_string(),
                format!("video=\"{}\"", video),
                "-f".to_string(),
                "dshow".to_string(),
                "-i".to_string(),
                format!("audio=\"{}\"", audio),
                "-filter_complex".to_string(),
                format!(
                    "\"[0]scale=1024:-1[desktop];[1]scale=160:-1 [cam]; [desktop][cam]overlay=5:5,drawtext=fontfile=msyh.ttf:text='{} %{{localtime}}':x=5:y=5:fontsize=12:fontcolor=red,drawtext=fontfile=msyh.ttf:text='%{{localtime}}':x=mod(100*t\\,w):y=abs(sin(t))*h*0.9:box=0:fontcolor=gray@0.4[video]; [2]anull[audio]\"",
                    watermark_text
                ),
                "-map".to_string(),
                "[video]".to_string(),
                "-map".to_string(),
                "[audio]".to_string(),
                "-b:v".to_string(),
                "550k".to_string(),
                "-c:v".to_string(),
                "libx264".to_string(),
                "-c:a".to_string(),
                "aac".to_string(),
                outfile.to_string(),
                // 输出摄像头截图,不需要截图可以注掉一下2行
                //"-map".to_string(), "1".to_string(), "-f".to_string(), "image2".to_string(), "-vf".to_string(), format!("fps={}", 1).as_str(),
                //"-update".to_string(), "1".to_string(), "tempc".to_string()
            ]
        }
        // 有摄像头和 无麦克风
        (None, Some(video)) => {
            is_open_camera = true;
            //ffmpeg -y -probesize 100M -f gdigrab -r 10 -i desktop -f dshow -i video="FULL HD 1080P Webcam" -filter_complex "[0]scale=1024:-1[desktop];[1]scale=160:120[cam];[desktop][cam]overlay=5:5,drawtext=text='%{localtime}':x=5:y=5:fontsize=12:fontcolor=red,drawtext=text='%{localtime}':x=mod(100*t\,w):y=abs(sin(t))*h*0.9:box=0:fontcolor=gray@0.4" -b:v 550k -c:v libx264 all.mp4
            vec![
                "ffmpeg".to_string(),
                "-hide_banner".to_string(),
                "-y".to_string(),
                "-probesize".to_string(),
                "10M".to_string(),
                "-f".to_string(),
                "gdigrab".to_string(),
                "-r".to_string(),
                "10".to_string(),
                "-t".to_string(),
                "28800".to_string(),
                "-i".to_string(),
                "desktop".to_string(),
                "-f".to_string(),
                "dshow".to_string(),
                "-i".to_string(),
                format!("video=\"{}\"", video),
                "-filter_complex".to_string(),
                format!(
                    "\"[0]scale=1024:-1[desktop];[1]scale=160:-1 [cam]; [desktop][cam]overlay=5:5,drawtext=fontfile=msyh.ttf:text='{} %{{localtime}}':x=5:y=5:fontsize=12:fontcolor=red,drawtext=fontfile=msyh.ttf:text='%{{localtime}}':x=mod(100*t\\,w):y=abs(sin(t))*h*0.9:box=0:fontcolor=gray@0.4\"",
                    watermark_text
                ),
                "-b:v".to_string(),
                "550k".to_string(),
                "-c:v".to_string(),
                "libx264".to_string(),
                "-c:a".to_string(),
                "aac".to_string(),
                outfile.to_string(),
                // 输出摄像头截图,不需要截图可以注掉一下2行
                //"-map".to_string(), "1".to_string(), "-f".to_string(), "image2".to_string(), "-vf".to_string(), format!("fps={}", 1),
                //"-update".to_string(), "1".to_string(), "tempc".to_string()
            ]
        }
        // 无摄像头 和 有麦克风
        (Some(audio), None) => {
            is_open_camera = false;
            //ffmpeg -y -probesize 100M -f gdigrab -r 10 -i desktop -f dshow -i audio="外部麦克风 (Realtek(R) Audio)" -filter_complex "[0]scale=1024:-1[desktop];[desktop]drawtext=text='%{localtime}':x=5:y=5:fontsize=12:fontcolor=red,drawtext=text='%{localtime}':x=mod(100*t\,w):y=abs(sin(t))*h*0.9:box=0:fontcolor=gray@0.4[video];[1]anull[audio]" -map "[video]" -map "[audio]" -b:v 550k -c:v libx264 -c:a aac all.mp4
            vec![
                "ffmpeg".to_string(),
                "-hide_banner".to_string(),
                "-y".to_string(),
                "-probesize".to_string(),
                "10M".to_string(),
                "-f".to_string(),
                "gdigrab".to_string(),
                "-r".to_string(),
                "10".to_string(),
                "-t".to_string(),
                "28800".to_string(),
                // "-fs".to_string(), "4096M".to_string(),
                "-i".to_string(),
                "desktop".to_string(),
                "-f".to_string(),
                "dshow".to_string(),
                "-i".to_string(),
                format!("audio=\"{}\"", audio),
                "-filter_complex".to_string(),
                format!(
                    "\"[0]scale=1024:-1[desktop];[desktop]drawtext=fontfile=msyh.ttf:text='{} %{{localtime}}':x=5:y=5:fontsize=12:fontcolor=red,drawtext=fontfile=msyh.ttf:text='%{{localtime}}':x=mod(100*t\\,w):y=abs(sin(t))*h*0.9:box=0:fontcolor=gray@0.4[video]; [1]anull[audio]\"",
                    watermark_text
                ),
                "-map".to_string(),
                "[video]".to_string(),
                "-map".to_string(),
                "[audio]".to_string(),
                "-b:v".to_string(),
                "550k".to_string(),
                "-c:v".to_string(),
                "libx264".to_string(),
                "-c:a".to_string(),
                "aac".to_string(),
                outfile.to_string(),
            ]
        }
        // 无摄像头和无麦克风
        _ => {
            is_open_camera = false;
            vec![
                "ffmpeg".to_string(),
                "-hide_banner".to_string(),
                "-y".to_string(),
                "-f".to_string(),
                "gdigrab".to_string(),
                "-i".to_string(),
                "desktop".to_string(),
                "-r".to_string(),
                "10".to_string(),
                "-t".to_string(),
                "28800".to_string(),
                // "-fs".to_string(), "4096M".to_string(),
                "-b:v".to_string(),
                "550k".to_string(),
                "-vf".to_string(),
                format!(
                    "\"scale=1024:-1,drawtext=fontfile=msyh.ttf:text='{} %{{localtime}}':fontsize=12:fontcolor=red:x=1:y=1,drawtext=fontfile=msyh.ttf:text='%{{localtime}}':x=mod(100*t\\,w):y=abs(sin(t))*h*0.9:box=0:fontcolor=gray@0.4\"",
                    watermark_text
                ),
                outfile.to_string(),
            ]
        }
    };
    if force_open_camera && !is_open_camera {
        return Err("无法开启摄像头!".to_string());
    }
    let mut fc = FfmpegController::new();
    let log_rx = fc.run_async(args);
    let log_thread = std::thread::spawn(move || {
        let mut n = 1;
        let mut is_first = true;
        for line in log_rx {
            eprintln!("{}", &line);
            // 输出的信息格式
            //frame=    0 fps=0.0 q=0.0  size=       0KiB time=N/A bitrate=N/A dup=0 drop=116 speed=N/A
            //frame=    5 fps=0.7 q=26.0 size=       0KiB time=00:00:00.30 bitrate=   1.3kbits/s dup=0 drop=125 speed=0.0448x
            if n % 2 == 0 {
                if line.starts_with("frame=") {
                    let p1 = line.find("time=");
                    let p2 = line.find("bitrate=");
                    if let (Some(start), Some(end)) = (p1, p2) {
                        let time_str = &line[start + "time=".len()..end - 4];
                        if !time_str.starts_with("N/A") {
                            if is_first {
                                let _ = send_msg(db::Message::new(
                                    db::MsgFlag::RECORDING,
                                    db::MsgStatus::START,
                                    format!("{}", pid),
                                    String::default(),
                                ));
                                is_first = false;
                            }

                            let _ = send_msg(db::Message::new(
                                db::MsgFlag::RECORDING,
                                db::MsgStatus::PROGRESS,
                                format!("{}", pid),
                                time_str.to_string(),
                            ));
                        }
                    }
                }
            }
            n += 1;
        }
        
    });

    let pid_file = format!("logs/{}.pid", pid);
    let pid_path = std::path::PathBuf::from(&pid_file);
    // 创建pid 文件,说明进程正在运行
    let _file = std::fs::File::create(pid_path.as_path());

    let stop_thread = std::thread::spawn(move || {
        while pid_path.exists() {
            //log::info!("=====ff7scre {} is running: {}", pid, &pid_path);
            std::thread::sleep(std::time::Duration::from_secs(1));
        }
        let ret = fc.wait_stop();
        ret
    });

    let ret = stop_thread.join().unwrap_or(0);
    log::info!("start_recording_1_3 stop_thread end,pid={},ret ={}", pid, ret);
    // 等待日志线程退出
    let _ = log_thread.join();
    log::info!("start_recording_2_3 log_thread end,pid={}", pid);

    let _ = send_msg(db::Message::new(
        db::MsgFlag::RECORDING,
        db::MsgStatus::SUCCESS,
        format!("{}", pid),
        format!("pid:{} ret  {}", pid, ret),
    ));

    log::info!("start_recording_3_3 all end ,pid={},ret ={}", pid, ret);

    Ok(pid)
}
